 class DivMover{
    _data={
        proxyElement:{},
    }
    _element={}
    //展示信息
    _iconName=""
    _url=""
    _icon=""
    _menu=null
    mouseDownX=0  //mousedown时鼠标的位置
    mouseDownY=0  
    mouseUpX=0  //mouseUp时鼠标的位置
    mouseUpY=0  
    x=0 //记录Element元素的位置
    y=0     
    elementPosistionBeforeUpdate={
        offsetLeft:0,
        offsetTop:0,
    }   
    margin=0
    element={}  //moveElement
    moveDistanceX = 100;// 移动距离
    moveDistanceY = 100;
    iconBackgroundColor = ""

    divContainer ={}
    constructor(divMoverElementClass,
                initPositionX,
                initPositionY,
                width,
                height,
                divContainer = null,
                icon = "",
                iconName = "",
                iconDivClassName,
                iconNameDivClassName,
                url = "https://www.baidu.com"){
        this._initObjValue(width,height,url,icon,iconName) //初始化DivMover的属性
        this._initDivMoverContent(divMoverElementClass,iconDivClassName,iconNameDivClassName)
        this._initDefineObserver(icon) //设置数据代理
        this._initDivPosition(initPositionX,initPositionY,divContainer) //初始化Div在Container中的位置
        this._initBindListener() //初始化监听器
        // console.log('constructor successful:',this.iconName)

    }
    _initObjValue(width,height,url,icon,iconName){
        //确定单元格宽度
        this.moveDistanceX=width
        this.moveDistanceY=height
        //url赋值
        this._url=url
        this._iconName = iconName
        this._icon = icon
    }
    _initDivMoverContent(divMoverElementClass,iconDivClassName,iconNameDivClassName){ //初始化DivMover的内容
        //初始化_element
        this._element = document.createElement("div")
        this._element.className = divMoverElementClass

        this._iconDiv = document.createElement("div")
        this._iconNameDiv = document.createElement("span")

        if(iconDivClassName) this._iconDiv.className = iconDivClassName
        if(iconNameDivClassName) this._iconNameDiv.className = iconNameDivClassName

        if(this._icon){
            let img = new Image();
            img.src = this._icon;
            this._iconDiv.style.backgroundImage = `url(${this._icon})`
            img.onerror =()=>{
                this._iconDiv.textContent = this._iconName.slice(0,1)
                const R = Math.floor(Math.random()*255)
                const G = Math.floor(Math.random()*255)
                const B = Math.floor(Math.random()*255)
                this._iconDiv.style.backgroundColor =  `rgba(${R},255,${B},1)`
                this.iconBackgroundColor = `rgba(${R},${G},${B},1)`
            }
        }else{
            this._iconDiv.style.backgroundColor =  'rgba(0,0,0,1)'
        } 
        this._iconNameDiv.textContent = this._iconName

        this._element.appendChild(this._iconDiv)
        this._element.appendChild(this._iconNameDiv)
    }
    _initDefineObserver(){
        Object.defineProperty(this,"element",{//修改DivMover的element属性，将能直接修改DivMover对应的DOM元素
            get(){
                return this._element
            },
            set(newValue){
                this._element.replaceWith(newValue)
                this._element = newValue
            }
        })
        Object.defineProperty(this,"margin",{//修改DivMover的element属性，将能直接修改DivMover对应的DOM元素
            get(){
                return this.element.style.margin
            },
            set(newValue){
                this.element.style.margin = newValue + "px"
                this._margin = newValue
            }
        })
        Object.defineProperty(this,"url",{//
            get(){
                return this._url
            },
            set(newValue){
                this._url = newValue
            }
        })
        Object.defineProperty(this,"iconDiv",{//icon
            get(){
                return this._iconDiv
            },
            set(newValue){
                this._iconDiv.replaceWith(newValue)
                this._iconDiv = newValue
            }
        })
        Object.defineProperty(this,"icon",{//修改DivMover的icon属性，将能直接修改对应的icon（backgroundImage）
            get(){
                return this._icon
            },
            set(newValue){
                for(let i of this.element.childNodes){
                    if(i.className==="divIcon") {
                        console.log('111')
                        i.style.backgroundImage = `url(${newValue})`
                        this._icon = newValue
                    }
                }
            }
        })
        Object.defineProperty(this,"iconName",{
            get(){
                return this._iconName
            },
            set(newValue){//修改
                for(let i of this.element.childNodes){
                    if(i.className==="divIconName") {
                        i.innerText = newValue
                    }
                }
                this._iconName = newValue
            }
        })
    }
    _initDivPosition(initPositionX,initPositionY,divContainer){
        //初始化Container
        this.divContainer = divContainer
        this.divContainer.container.appendChild(this.element)
        
        //初始化元素显示位置
        this.updateElementPosition(initPositionX,initPositionY)
        //记录元素位置
        this.x = this.elementPosistionBeforeUpdate.offsetLeft = this.element.offsetLeft;
        this.y = this.elementPosistionBeforeUpdate.offsetTop = this.element.offsetTop;
        //占据container
        let flag = this.divContainer.occupyAreaContainDivMover(
            this.element.offsetLeft,
            this.element.offsetTop,
            this.element.offsetWidth+this.margin,
            this.element.offsetHeight+this.margin,this)
        if(flag === false){
            this.element.style.display="none"
            throw new Error('container occupy error');
        }

        //生成拖拽预期落点方块
        this.moveDisplay = document.createElement("div")
        this.moveDisplay.id = 'divMoveDisplay';
        this.divContainer.container.appendChild(this.moveDisplay)
    }
    _initBindListener(){
        // 绑定事件处理函数的this指针指向本实例对象
        this.onMouseDown = this.onMouseDown.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.onMouseUp = this.onMouseUp.bind(this); 
        this.showOptionMenu = this.showOptionMenu.bind(this)
        this.element.addEventListener("mousedown",this.onMouseDown)
        //鼠标右键菜单
        this.element.addEventListener('contextmenu',this.showOptionMenu)
    }

    /*
        主体逻辑功能
    */
    onMouseDown(e){
        if(e.button === 2){
            return
        }
        console.log('onMouseDown',e)
        this.mouseDownX = e.clientX; //mouse posistion
        this.mouseDownY = e.clientY;
        document.addEventListener("mousemove",this.onMouseMove)//初始化监听器
        document.addEventListener("mouseup",this.onMouseUp)
        this.element.style.cursor = 'grabbing'
        this.divContainer.clearArea( //移动Div时,DivMover脱离DivContainer
            this.elementPosistionBeforeUpdate.offsetLeft,
            this.elementPosistionBeforeUpdate.offsetTop,
            this.element.offsetWidth+this.margin,
            this.element.offsetHeight+this.margin
        )

        this.showMoveDisplay()//展示moveDisplay(Div移动预期落点)
        // console.log('this.mouseDownX',this.mouseDownX,'this.mouseDownY ',this.mouseDownY )
        return        
    }

    onMouseMove(e){
        console.log('onMouseMove')
        const deltaX = e.clientX - this.mouseDownX;
        const deltaY = e.clientY - this.mouseDownY + this.totalScrollPixels;
        this.totalScrollPixels = 0
        // 更新元素的位置
        this.updateElementPosition(this.x + deltaX,this.y + deltaY)
        
        // 计算预期落点位置
        const finalX = this.x + deltaX;
        const finalY = this.y + deltaY;
        const snapX = Math.round(finalX / this.moveDistanceX) * this.moveDistanceX;
        const snapY = Math.round(finalY / this.moveDistanceY) * this.moveDistanceY;

        // 更新吸附落点元素的位置
        this.moveDisplay.style.left = `${snapX}px`;
        this.moveDisplay.style.top = `${snapY}px`;

        // 判断预期落点是否被占用
        const isOccupied = this.divContainer.checkOccupy(snapX, snapY, 
            this.element.offsetWidth + this.margin, 
            this.element.offsetHeight+ this.margin );
        switch(isOccupied){
            case "crossBorder":
                this.moveDisplay.style.borderColor = 'red';
                this.moveDisplay.style.backgroundColor = 'red';
                break;
            case "free":
                this.moveDisplay.style.borderColor = 'greenyellow';
                this.moveDisplay.style.backgroundColor = 'greenyellow';
                break;
            default:
                this.moveDisplay.style.borderColor = 'yellow';
                this.moveDisplay.style.backgroundColor = 'yellow';
                break;
        }
    }

    onMouseUp(e){
        console.log('onMouseUp')
        //获取元素落点位置
        const finalX = this.x + (e.clientX - this.mouseDownX);
        const finalY = this.y + (e.clientY - this.mouseDownY);

        //位置未改变时,跳转到对应网址
        if(finalX === this.elementPosistionBeforeUpdate.offsetLeft && finalY === this.elementPosistionBeforeUpdate.offsetTop){
            this.handlerClick()
        }
        // 更新 x 和 y 为 调整到阈值的整数倍的值
        this.x = Math.round(finalX / this.moveDistanceX) * this.moveDistanceX;
        this.y =  Math.round(finalY / this.moveDistanceY) * this.moveDistanceY;

        //碰撞检测
        let occupyFlag=this.divContainer.checkOccupy(
            this.x,this.y,
            this.element.offsetWidth+this.margin,
            this.element.offsetHeight+this.margin
        )
        switch(occupyFlag){
            case "crossBorder":
                //重回拖拽前
                this.divContainer.occupyAreaContainDivMover(//占领拖拽前位置
                    this.elementPosistionBeforeUpdate.offsetLeft,
                    this.elementPosistionBeforeUpdate.offsetTop,
                    this.element.offsetWidth+this.margin,
                    this.element.offsetHeight+this.margin,
                    this
                )
                this.resetElementPostion()//重设Div的位置
                break;
            case "free":
                this.divContainer.occupyAreaContainDivMover(//占领新位置
                    this.x,this.y,
                    this.element.offsetWidth+this.margin,
                    this.element.offsetHeight+this.margin,
                    this
                )
                this.updataPosition(finalX,finalY)
                this.updateElementPosition(this.x,this.y)
                break;
            default:
                console.log('合并为文件夹逻辑')
                this.divMoverCombineOP(occupyFlag)
                break;
        }

        //移除拖动监听器
        document.removeEventListener("mousemove", this.onMouseMove);
        document.removeEventListener("mouseup", this.onMouseUp);
        this.element.style.cursor = 'grab'       // 改变鼠标样式
        this.moveDisplay.style.display = 'none'; // 隐藏吸附落点指示元素
    }

    /*
        可重写函数
    */
    divMoverCombineOP(occupyFlag){
        this.divContainer.divMoverCombine(occupyFlag,this)
    }
    
    /*
        MoveDisplay落点展示功能
    */
    showMoveDisplay(){//展示ExpectPositionDiv
        this.moveDisplay.style.width = `${this.element.offsetWidth}px`; //落点预期指示元素位置
        this.moveDisplay.style.height = `${this.element.offsetHeight}px`;
        this.moveDisplay.style.left =  `${Math.round(this.x/ this.moveDistanceX) * this.moveDistanceX}px`;
        this.moveDisplay.style.top =  `${Math.round(this.y / this.moveDistanceY) * this.moveDistanceY}px`;
        this.moveDisplay.style.backgroundColor = 'greenyellow' //落点预期指示元素位置
        this.moveDisplay.style.display = 'block';              //显示落点预期指示元素
    }
    
    /*
        Option功能
    */
    showOptionMenu(e){ //展示选项框
        console.log("showOptionMenu(e)",e)
        console.log('e.srcElement ',e.srcElement ) 

        Array.from(document.querySelectorAll(".moverMenu.visible")).forEach(element => element.remove()) //只允许一个OptionMenu存在
        e.stopPropagation() // 阻止事件冒泡
        e.preventDefault(); // 阻止默认的上下文菜单


        let menu = document.createElement('div');//创建OptionMenu
        menu.className = 'moverMenu'
        menu.style.display = 'block';
        menu.style.position = "absolute"
        const rect = this.divContainer.container.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        menu.style.left = x + 'px';//点击处显示menu
        menu.style.top = y + 'px';
        menu.style.maxHeight="600px";

        let optionJson = [
            {
                optionName:"changeIconName",
                optionMethod(divMover){
                    let userInput = prompt("请输入新的图标名称：", "");
                    // 检查用户是否点击了取消或关闭按钮
                    if (userInput === null) {
                        alert("你取消了输入！");
                    } else {
                        divMover.iconName = userInput
                    }
                }
            }
        ]

        menu.style.display="grid"
        menu.style.gridTemplateRows = `repeat(${optionJson.length}, 1fr)`

        for(let op of optionJson){
            let opDiv = document.createElement("button")
            opDiv.textContent = op.optionName
            // opDiv.style.fontSize = "5vm"
            opDiv.style.margin = "6px"
            opDiv.style.maxHeight="100px";
            opDiv.style.backgroundColor = "yellow"
            opDiv.onclick = ()=>{
                op.optionMethod(this)
                //消除对话框
                Array.from(document.querySelectorAll(".moverMenu.visible")).forEach(element => element.remove()) 
            }
            menu.appendChild(opDiv)
        }

        this.divContainer.container.appendChild(menu)
        this.menu = menu

        // 等待浏览器重绘后添加 visible 类
        requestAnimationFrame(() => {
            requestAnimationFrame(() => {
                menu.classList.add('visible');
            });
        });
        document.addEventListener('click', this.handleDocumentClick.bind(this));//清除监听


    }

    handleDocumentClick(e) { //处理Option选项框
        // 如果点击的是菜单内部，则不处理
        if (this.menu && this.menu.contains(e.target)) {
            return;
        }
        // 如果点击的是菜单外部，则清除菜单
        this.clearMoverMenu();
    }

    clearMoverMenu(){//清除选项框
        if (this.menu) {
            this.menu.remove(); // 移除之前的菜单
            this.menu = null; // 清除引用
        }
        document.removeEventListener('click', this.handleDocumentClick.bind(this));//监听 -> 清除操作
    }

    /*
        DivMover更新位置功能
    */
    updataPosition(finalX,finalY){
        // 更新 offsetLeft 和 offsetTop 为 调整到阈值的整数倍的值
        this.x = Math.round(finalX / this.moveDistanceX) * this.moveDistanceX;
        this.y =  Math.round(finalY / this.moveDistanceY) * this.moveDistanceY;
        this.elementPosistionBeforeUpdate.offsetLeft = this.x
        this.elementPosistionBeforeUpdate.offsetTop  = this.y
    }
    updateElementPosition(PositionX,PositionY){//设置图形位置
        //更新元素的位置
        this.element.style.left = `${PositionX}px`;
        this.element.style.top = `${PositionY}px`;
        // console.log(' this.element.style.left', this.element.style.left,'this.element.style.top',this.element.style.top)
    }
    resetElementPostion(){
        this.element.style.left = `${this.elementPosistionBeforeUpdate.offsetLeft}px`;
        this.element.style.top = `${this.elementPosistionBeforeUpdate.offsetTop}px`;
        this.x = this.elementPosistionBeforeUpdate.offsetLeft
        this.y = this.elementPosistionBeforeUpdate.offsetTop
    }

    handlerClick(){
        window.open(this.url, '_blank');
    }

    /*
        DivMover销毁函数
     */
    destroyDivMover() {
        //移除拖动监听器
        this.element.removeEventListener("mousedown", this.onMouseDown);
        document.removeEventListener("mousemove", this.onMouseMove);
        document.removeEventListener("mouseup", this.onMouseUp);
        this.element.style.display = "none"
        console.warn(this,'————————————————DivMoverDestory destory---------------')
    }

}